//
// Created by dave on 18-7-28.
//

#pragma once
#include <memory>
#include <glog/logging.h>
#include <boost/asio.hpp>
#include <boost/regex.hpp>
#include "channel_handler.h"

using boost::asio::ip::tcp;

class channel : public std::enable_shared_from_this<channel> {
public:
    explicit channel(tcp::socket sock, std::shared_ptr<channel_handler> handler, boost::asio::io_context::strand& strand)
            : sock_(std::move(sock)), handler_(handler), strand_(strand) {
        closed_ = false;
    }

    void close() {
        if (!closed_) {
            try {
                sock_.close();
            } catch (boost::system::system_error& err) {
                LOG(ERROR) << "关闭失败：" << err.what();
            }
            closed_ = true;
        }
    }

    void write(std::vector<unsigned char>&& buf) {
        auto self(shared_from_this());
        if (closed_) {
            handler_->on_error(self, boost::asio::error::eof);
            return;
        }

        boost::asio::async_write(sock_, boost::asio::buffer(buf), [this, self](boost::system::error_code ec, std::size_t length) {
            if (ec) {
                handler_->on_error(self, ec);
            }
        });
    }

    bool closed() {
        return closed_;
    }

    void start() {
        auto self(shared_from_this());
        boost::asio::async_read_until(sock_, buffer_, std::bind(&channel::match, shared_from_this(), std::placeholders::_1, std::placeholders::_2),
                [this, self](boost::system::error_code ec, std::size_t length) {
                    auto ptr = boost::asio::buffer_cast<const unsigned char*>(buffer_.data());
                    std::vector<unsigned char> vec(length);
                    for (int i = 0; i < length; i++) {
                        vec[i] = ptr[i];
                    }
                    buffer_.consume(length);
                    on_read(ec, std::move(vec));
                    if (!closed_) {
                        start();
                    }
                    //boost::asio::bind_executor(strand_, std::bind(&channel::on_read, this, std::placeholders::_1, std::placeholders::_2))(ec, std::move(vec));
                });
    }

private:
    typedef boost::asio::buffers_iterator<boost::asio::streambuf::const_buffers_type> iterator;

    std::pair<iterator, bool> match(iterator start, iterator stop) {
        int count = 0;
        iterator i = start;
        iterator last = stop;
        while (i != stop) {
            count += 1;
            if (count >= 4 * 1024) {
                return std::make_pair(i, true);
            }

            if (*i == 0x7E) {
                last = i;
            }
            i++;
        }

        if (last != stop && last != start) {
            iterator pre = last;
            if (*--pre == 0x7E) {
                return std::make_pair(++pre, true);
            } else {
                return std::make_pair(++last, true);
            }
        } else {
            return std::make_pair(start, false);
        }
    }

    void on_read(boost::system::error_code ec, std::vector<unsigned char> data) {
        auto self(shared_from_this());
        if (ec) {
            if (ec == boost::asio::error::eof) {
                closed_ = true;
                handler_->on_close(self, ec);
            } else {
                handler_->on_error(self, ec);
            }
        } else if (data.size() >= 1024 * 3) {
            LOG(WARNING) << "忽略了" << data.size();
            buffer_.consume(data.size()); // 直接忽略
        } else if (data.size() > 0) {
            handler_->on_read(self, data.data(), static_cast<int>(data.size()));
        }
    }

private:
    tcp::socket sock_;
    std::shared_ptr<channel_handler> handler_;
    boost::asio::streambuf buffer_{5 * 1024};
    boost::asio::io_context::strand& strand_;
    bool closed_;
};

